/*************************************************
 * 
 * Copyright by Bruno Eberhard
 * 
 * Created on 29.07.2005
 *
 *************************************************/
package fightplan.business;

import java.io.Serializable;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import javax.swing.event.ListDataListener;

import fightplan.algorithmen.Layout;
import fightplan.algorithmen.LayoutFactory;
import fightplan.algorithmen.ko.FinalrundePlanFactory;
import fightplan.algorithmen.ko.KOLayoutFactory;
import fightplan.algorithmen.ko.KOPlanFactory;
import fightplan.bindx.GLinkedListModel;
import fightplan.bindx.GObservableList;
import fightplan.business.util.AgeUtil;
import fightplan.business.util.BeltUtil;
import fightplan.business.util.WeightUtil;

public class Category implements Serializable {
	private static final long serialVersionUID = 1L;

	protected static DateFormat TIME_FORMATTER = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM,
			Locale.GERMAN);

	protected CategoryType categoryType = CategoryType.KATA;
	protected Gender gender = Gender.H;
	protected String name = null;

	protected List<AbstractFighter> seeds = new ArrayList<AbstractFighter>();

	private static KOPlanFactory koPlanFactory = new KOPlanFactory();
	private static FinalrundePlanFactory finalrundePlanFactory = new FinalrundePlanFactory();

	private Plan plan;
	private boolean min2Sheets = true;
	private boolean trostrunde = true;
	private boolean finalrunde;

	private static LayoutFactory layoutFactory = new KOLayoutFactory();
	private transient Layout layout;

	protected Date planTime;
	protected Date printTime;

	private long stamp;

	private AgeType ageType = AgeType.ALL;
	private int ageStart = AgeUtil.MIN_AGE;
	private int ageEnd = AgeUtil.MAX_AGE;
	private int minWeight = WeightUtil.MIN_WEIGHT;
	private int maxWeight = WeightUtil.MAX_WEIGHT;
	private int minBelt = BeltUtil.MIN_BELT;
	private int maxBelt = BeltUtil.MAX_BELT;

	private GObservableList<AbstractFighter> fighters = new GLinkedListModel<AbstractFighter>();

	private int skrId = -1;

	public Category() {
		stamp = System.nanoTime();
	}

	// get / set

	public String getName() {
		return name;
	}

	public void setName(String name) {
		if (getDescription().equals(name)) {
			this.name = null;
		} else {
			this.name = name;
		}
	}

	public int getCategoryId() {
		return skrId;
	}

	public void setCategoryId(int skrId) {
		this.skrId = skrId;
	}

	public boolean hasSpecialName() {
		return name != null && name.length() > 0;
	}

	public void clearSpecialName() {
		name = null;
	}

	public void plan() {
		if (!isFinalrunde()) {
			plan = koPlanFactory.plan(this, trostrunde);
		} else {
			plan = finalrundePlanFactory.plan(this);
		}

		planTime = new Date();
		layout = null;
	}

	public void removePlan() {
		plan = null;
		layout = null;
		planTime = null;
		printTime = null;
	}

	public String getPlanTime() {
		return planTime != null ? TIME_FORMATTER.format(planTime) : "";
	}

	public void setPrintTime(Date date) {
		this.printTime = date;
	}

	public String getPrintTime() {
		return printTime != null ? TIME_FORMATTER.format(printTime) : "";
	}

	public Layout getLayout() {
		if (layout == null && plan != null) {
			layout = layoutFactory.layout(plan, min2Sheets);
		}
		return layout;
	}

	public Plan getPlan() {
		return plan;
	}

	public boolean isPlanned() {
		return plan != null;
	}

	public List<AbstractFighter> getSeeds() {
		return seeds;
	}

	public void setSeeds(List<AbstractFighter> seeds) {
		this.seeds = seeds;
	}

	public boolean isMin2Sheets() {
		return min2Sheets;
	}

	public void setMin2Sheets(boolean min2Sheets) {
		this.min2Sheets = min2Sheets;
	}

	public boolean isTrostrunde() {
		return trostrunde;
	}

	public void setTrostrunde(boolean trostrunde) {
		this.trostrunde = trostrunde;
	}

	public boolean isFinalrunde() {
		return finalrunde;
	}

	public void setFinalrunde(boolean finalrunde) {
		this.finalrunde = finalrunde;
	}

	/*
	 * public String getTechnialName() { String name = categoryType.getName() +
	 * " " + gender.getNameLong(); if (!CategoryType.KATA.equals(categoryType))
	 * { name += " [" + getWeightRangeFormatted() + "]"; } name += " (" +
	 * getAgeRangeFormatted() + ")"; name += " {" + getBeltRangeFormatted() +
	 * "}"; return name; }
	 */

	public String getDescription() {
		String description = categoryType.getName() + " " + gender.getNameLong();
		String spec = "";
		if (ageStart > AgeUtil.MIN_AGE || ageEnd < AgeUtil.MAX_AGE || minWeight > WeightUtil.MIN_WEIGHT
				|| maxWeight < WeightUtil.MAX_WEIGHT || minBelt > BeltUtil.MIN_BELT || maxBelt < BeltUtil.MAX_BELT) {

			if (ageStart > AgeUtil.MIN_AGE || ageEnd < AgeUtil.MAX_AGE) {
				spec = AgeUtil.formatRangeForDescription(ageStart, ageEnd);
			}

			if (minWeight > WeightUtil.MIN_WEIGHT || maxWeight < WeightUtil.MAX_WEIGHT) {
				if (spec.length() > 0)
					spec += ", ";
				spec += WeightUtil.formatRangeForDescription(minWeight, maxWeight);
			}

			if (minBelt > BeltUtil.MIN_BELT || maxBelt < BeltUtil.MAX_BELT) {
				if (spec.length() > 0)
					spec += ", ";
				spec += BeltUtil.formatRangeForDescription(minBelt, maxBelt);
			}

			description += "  (" + spec.trim() + ")";
		}
		return description;
	}

	public String getNameOrDescription() {
		if (hasSpecialName()) {
			return name;
		} else {
			return getDescription();
		}
	}

	public GObservableList<AbstractFighter> getFighters() {
		return fighters;
	}

	public Gender getGender() {
		return gender;
	}

	public void setGender(Gender gender) {
		this.gender = gender;
	}

	public CategoryType getCategoryType() {
		return categoryType;
	}

	public boolean isKumite() {
		return getCategoryType().isKumite();
	}

	public void setCategoryType(CategoryType categoryType) {
		this.categoryType = categoryType;
	}

	public boolean isAgeOpen() {
		return ageEnd >= AgeUtil.MAX_AGE;
	}

	public int getAgeEnd() {
		return ageEnd;
	}

	public void setAgeEnd(int ageEnd) {
		this.ageEnd = ageEnd;
	}

	public int getAgeStart() {
		return ageStart;
	}

	public void setAgeStart(int ageStart) {
		this.ageStart = ageStart;
	}

	public AgeType getAgeType() {
		return ageType;
	}

	public void setAgeType(AgeType ageType) {
		this.ageType = ageType;
	}

	public int getYearStart() {
		// TODO rewrite categories to year
		return new Date().getYear() + 1900 - ageEnd;
	}

	public int getYearEnd() {
		return new Date().getYear() + 1900 - ageStart;
	}

	public int getMaxWeight() {
		return maxWeight;
	}

	public void setMaxWeight(int maxWeight) {
		this.maxWeight = maxWeight;
	}

	public int getMinWeight() {
		return minWeight;
	}

	public void setMinWeight(int minWeight) {
		this.minWeight = minWeight;
	}

	public String getBeltRangeFormatted() {
		return BeltUtil.formatRange(minBelt, maxBelt);
	}

	public void setBeltRangeFormatted(String formattedString) {
		int tempMinBelt = BeltUtil.parseRangeStart(formattedString); // save if
																		// maxBelt
																		// throws
																		// Error
		maxBelt = BeltUtil.parseRangeEnd(formattedString);
		minBelt = tempMinBelt;
	}

	public String getAgeRangeFormatted() {
		return AgeUtil.formatRange(ageStart, ageEnd);
	}

	public void setAgeRangeFormatted(String formattedString) {
		int tempAgeStart = AgeUtil.parseRangeStart(formattedString);
		ageEnd = AgeUtil.parseRangeEnd(formattedString);
		ageStart = tempAgeStart;
	}

	public int getMaxBelt() {
		return maxBelt;
	}

	public int getMinBelt() {
		return minBelt;
	}

	public void setMaxBelt(int maxBelt) {
		this.maxBelt = maxBelt;
	}

	public void setMinBelt(int minBelt) {
		this.minBelt = minBelt;
	}

	public String getWeightRangeFormatted() {
		return WeightUtil.formatRange(minWeight, maxWeight);
	}

	public void setWeightRangeFormatted(String formattedString) {
		int tempMinWeight = WeightUtil.parseRangeStart(formattedString);
		maxWeight = WeightUtil.parseRangeEnd(formattedString);
		minWeight = tempMinWeight;
	}

	public boolean compatible(AbstractFighter abstractFighter) {

		if (abstractFighter instanceof Fighter) {
			if (isTeam())
				return false;

			Fighter fighter = (Fighter) abstractFighter;

			if (!getGender().comaptible(fighter.getGender()))
				return false;

			if (fighter.getBelt() < minBelt)
				return false;
			if (fighter.getBelt() > maxBelt)
				return false;

			if (fighter.getYear() < getYearStart())
				return false;
			if (fighter.getYear() > getYearEnd())
				return false;

			if (isKumite() && (minWeight > WeightUtil.MIN_WEIGHT || maxWeight < WeightUtil.MAX_WEIGHT)) {
				if (fighter.getWeight() <= 0)
					return false;
				if (fighter.getWeight() < minWeight)
					return false;
				if (fighter.getWeight() > maxWeight)
					return false;
			}
		} else if (abstractFighter instanceof Team) {
			Team team = (Team) abstractFighter;
			return this.equals(team.getCategory());

		}
		return true;
	}

	public boolean isTeam() {
		return categoryType.isTeam();
	}

	public int checkIntegrity() {
		int errors = 0;
		for (AbstractFighter fighter : getFighters()) {
			if (!compatible(fighter)) {
				errors++;
			}
		}
		return errors;
	}

	public CategoryStatus getStatus() {
		if (printTime != null) {
			return CategoryStatus.PRINTED;
		} else if (planTime != null) {
			return CategoryStatus.DRAWN;
		} else if (getWarning() != null) {
			return CategoryStatus.INCOMPLETE;
		} else {
			return CategoryStatus.READY;
		}
	}

	public String getStatusText() {
		if (printTime != null) {
			return "Gedruckt";
		} else if (planTime != null) {
			return "Ausgelost";
		} else if (getWarning() != null) {
			return getWarning();
		} else {
			return "Bereit für Auslosung";
		}
	}

	public String getWarning() {
		if (isTrostrunde() && getFighters().size() < 5) {
			return "Zu wenig Teilnehmer für eine Trostrunde";
		} else if (isMin2Sheets() && getFighters().size() < 4) {
			return "Zu wenig Teilnehmer für zwei Blätter";
		} else if (getFighters().size() < 2) {
			return "Es müssen mindestens 2 Teilnehmer eingeschreiben sein";
		}
		return null;
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof Category))
			return false;
		Category c2 = (Category) obj;
		return stamp == c2.stamp;
	}

	// Fightersprovider (delegating)

	public void addFighter(AbstractFighter fighter) {
		fighters.add(fighter);
	}

	public void addListDataListener(ListDataListener listener) {
		fighters.addListDataListener(listener);
	}

	public boolean contains(AbstractFighter fighter) {
		return fighters.contains(fighter);
	}

	public void removeFighter(AbstractFighter fighter) {
		fighters.remove(fighter);
	}

	public void removeListDataListener(ListDataListener listener) {
		fighters.removeListDataListener(listener);
	}

	public Iterator<AbstractFighter> fightersIterator() {
		return fighters.iterator();
	}

}
